
import { toRaw, unref, isRef } from 'vue'

/**
 * 对象 key 的 类型
 */
export type IStateKey = string | number | symbol

/**
 * 任意一个对象
 */
export type IAnyObject = {[key: IStateKey]: any}

/**
 * 任意一个数组
 */
export type IAnyArray = Array<any>

/**
 * 任意一个函数，返回对象
 */
export type IAnyFunctionObject = (...arg: Array<any>) => IAnyObject

/**
 * 任意一个函数，返回数组
 */
export type IAnyFunctionArray = (...arg: Array<any>) => IAnyArray

/**
 * 对象或者函数
 */
export type IObjectOrFunction = IAnyObject | IAnyFunctionObject

/**
 * 数组或者函数
 */
export type IArrayOrFunction = IAnyArray | IAnyFunctionArray

/**
 * 对象或者数组
 */
export type IObjectOrArray = IAnyObject | IAnyArray

/**
 * 
 */
export default class NFState {
  #id: IStateKey
  #isLog: boolean
  #_value: IObjectOrFunction
  #isState = true
  #isObject = true

  constructor (obj: IObjectOrFunction, id: IStateKey = '_state', isLog = false) {
    this.#id = id 
    this.#isLog = isLog
    // 记录初始值
    this.#_value = obj
    if (typeof obj === 'function'){
      // 执行函数，浅层拷贝，设置属性
      Object.assign(this, obj())
    }
    else {
      // 假设单层属性，浅层拷贝，设置属性
      Object.assign(this, this.#_value)
    }
  }

  /**
   * 获取初始值
   */
  get $value() {
    const _this = toRaw(this)
    return _this.#_value
  }
  
  /**
   * 获取ID
   */
  get $id() {
    const _this = toRaw(this)
    return _this.#id
  }

  /**
   * 获取是否需要记录日志
   */
  get $isLog() {
    const _this = toRaw(this)
    return _this.#isLog
  }

  /**
   * 验证是不是状态
   */
  get $isState() {
    const _this = toRaw(this)
    return _this.#isState
  }
  /**
   * 验证是不是对象类型的状态
   */
  get $isObject() {
    const _this = toRaw(this)
    return _this.#isObject
  }

  // 操作状态的函数

}


/**
 * 使用基类实现共用函数
 */
export class NFState2 {
  #isState = true
  #isObject = true
  #_value: IObjectOrFunction

  constructor (obj: IObjectOrFunction) {
    if (typeof obj === 'function'){
      // 记录初始函数
      this.#_value = obj
      // 执行函数，浅层拷贝，设置属性
      Object.assign(this, obj())
    }
    else {
      // 记录初始值的副本，浅层拷贝，只支持单层属性
      this.#_value = Object.assign(obj)
      // 浅层拷贝，设置属性
      Object.assign(this, obj)
    }
  }
  
  /**
   * 验证是不是状态
   */
  get $isState() {
    const _this = toRaw(this)
    return _this.#isState
  }
  /**
   * 验证是不是对象类型的状态
   */
  get $isObject() {
    const _this = toRaw(this)
    return _this.#isObject
  }

  // 操作状态的函数
  /**
   * 获取初始值，如果是函数的话，会调用函数返回结果
   */
  get $value() {
    const val = toRaw(this).#_value
    const re = typeof val === 'function' ? val() : val
    return re
  }

  /**
   * 恢复初始值
   */
  $reset() {
    // 模板里面触发的事件，没有 this
    if (this) {
      copy(toRaw(this), this.$value)
    }
  }

  /**
   * 设置新值
   */
  set $state(value: IAnyObject) {
    // 要不要判断 value 的属性是否完整？
    copy(toRaw(this), value)
     
  }

  /**
   * 替换部分属性，只支持单层
   */
  async $patch(obj: IObjectOrFunction) {
    if (typeof obj === 'function') {
      // 回调，不接收返回值
      await obj(this)
    } else {
      // 赋值
      copy(this, obj)
    }
  }
}

/**
 * 以 target 的属性为准，进行赋值。支持部分深层copy
 * * 如果属性是数组的话，可以保持响应性，但是不支持深层copy
 * * 如果 有 $state，会调用。
 * @param target 目标
 * @param source 源
 */
function copy(target: IAnyObject, source: IAnyObject) {
  const _this = target
  const _source = toRaw(source)

  // 以 原定状态的属性为准遍历，不增加、减少属性
  for (const key in _this) {
    const _val = unref(_source[key]) // 应对 ref 取值
    const _target = _this[key]

    if (_val) { // 如果有值
      if (_target.$state) { // 对象、数组可以有 $state。
        _target.$state = _val
      } else {
        if (Array.isArray(_target)) { // 数组的话，需要保持响应性
          _target.length = 0
          if (Array.isArray(_val)) // 来源是数组，拆开push
            _target.push(..._val)
          else 
            _target.push(_val) // 不是数组直接push

        } else if (typeof _target === 'object') { // 对象，浅拷
          Object.assign(_this[key], _val)
          // 这里调用 copy 的话，可以递归深拷吧，不过不想做深拷。
        } else {
          if (isRef(_this[key])) { // 还得考虑 ref
            _this[key].value = _val
          } else {
            _this[key] = _val // 其他，赋值
          }
        }
      }
       
    }
  }
}

class Ret extends NFState2 {
  sonName: string

  constructor(obj: IObjectOrFunction) {
    super(obj) // 调用父类的constructor()
    this.sonName = '子类的属性'
  }

}

const a = new Ret({})
 